#include "semaphores.h"
#include "os_mem.h"
#include "sw_interrupt.h"


static err_t sem_create_internal(sem_t **sem_pp, sem_opt_t sem_opt)
{
    sem_t *ptr = (sem_t *)malloc(sizeof(sem_t));
    if (!ptr) { return ERR_NO_MEM; }

    ptr->sem_lock = SEM_UNLOCKED;
    ptr->process_list.next_process = &ptr->process_list;
    ptr->process_list.prev_process = &ptr->process_list;
    ptr->sem_opt = sem_opt;

    *sem_pp = ptr;
    return ERR_SUCCESS;
}

err_t sem_create2(sem_t *ptr, bool shared, bool blocked)
{
    if (!ptr) { return ERR_INVALID; }

    if (blocked) {
        ptr->sem_lock = SEM_LOCKED;
    } else {
       ptr->sem_lock = SEM_UNLOCKED;
    }

    ptr->process_list.next_process = &ptr->process_list;
    ptr->process_list.prev_process = &ptr->process_list;

    if (shared) {
        ptr->sem_opt = SEM_SHARED;
    } else {
        ptr->sem_opt = SEM_LOCAL;
    }

    return ERR_SUCCESS;
}

err_t sem_create(sem_t **sem_pp)
{
    return sem_create_internal(sem_pp, SEM_LOCAL);
}

err_t sem_create_shared(sem_t **sem_pp)
{
    return sem_create_internal(sem_pp, SEM_SHARED);
}

err_t sem_create_blocked(sem_t **sem_pp)
{
    err_t sem_error;

    if (!sem_pp) { return ERR_INVALID; }

    sem_error = sem_create(sem_pp);
    if (sem_error == ERR_SUCCESS) {
	(*sem_pp)->sem_lock = SEM_LOCKED;
    }

    return sem_error;
}

err_t sem_release(sem_t *sem_ptr)
{
    if (!sem_ptr) {
        return ERR_INVALID;
    }

    free((uint8_t *)sem_ptr);
    return ERR_SUCCESS;
}



err_t sem_lock(sem_t * sem_ptr)
{
    if (!sem_ptr) {
	return ERR_INVALID;
    }

    raise_sw_interrupt(SW_INT_SEMAPHORE_LOCK, (uint32_t)sem_ptr);
    return sem_ptr->err_code;
}

err_t sem_unlock(sem_t *sem_ptr)
{
    if (!sem_ptr) {
	return ERR_INVALID;
    }

    // @todo: Check if process owns the semaphore??
    raise_sw_interrupt(SW_INT_SEMAPHORE_UNLOCK, (uint32_t)sem_ptr);
    return sem_ptr->err_code;

}


err_t sem_trylock(sem_t *sem_ptr)
{
    if (!sem_ptr) {
	return ERR_INVALID;
    }

    if (sem_ptr->sem_lock == SEM_LOCKED) {
	return ERR_BUSY;
    }

    raise_sw_interrupt(SW_INT_SEMAPHORE_TRYLOCK, (uint32_t)sem_ptr);

    return sem_ptr->err_code;
}
